﻿using System.Diagnostics;
using YiJingFramework.Core;
using YiJingFramework.Painting.Deriving.Extensions;

namespace PBIW.Operating
{
    public sealed class PlumBlossomGenerator
    {
        private static Painting GetTrigram(int innateNumber)
        {
            Debug.Assert(innateNumber is >= 1 and <= 8);

            innateNumber -= 1;
            var lower = innateNumber >> 2;
            var middle = (innateNumber >> 1) - lower * 2;
            var upper = innateNumber - lower * 4 - middle * 2;
            return new Painting(
                new YinYang(lower == 0),
                new YinYang(middle == 0),
                new YinYang(upper == 0));
        }

        private readonly HashSet<Painting> generateOverlappingByChanged;

        private static readonly IEnumerable<Painting> QianAndKun = new Painting[] {
            new Painting(YinYang.Yang, YinYang.Yang, YinYang.Yang, YinYang.Yang, YinYang.Yang, YinYang.Yang),
            new Painting(YinYang.Yin, YinYang.Yin, YinYang.Yin, YinYang.Yin, YinYang.Yin, YinYang.Yin) };

        public PlumBlossomGenerator(bool generateOverlappingByChangedForQianAndKun = true)
            : this(QianAndKun) { }

        public PlumBlossomGenerator(IEnumerable<Painting> generateOverlappingByChanged)
        {
            this.generateOverlappingByChanged = new HashSet<Painting>(generateOverlappingByChanged);
        }

        public PlumBlossomHexagrams GenerateHexagrams(
            int upper, int lower, int changingLine)
        {
            upper = upper.ModToPositive(8);
            lower = lower.ModToPositive(8);
            changingLine = changingLine.ModToPositive(6);

            Painting upperPainting = GetTrigram(upper);
            Painting lowerPainting = GetTrigram(lower);

            IEnumerable<YinYang> originalLines = lowerPainting.Concat(upperPainting);
            Painting originalPainting = new Painting(originalLines);
            Painting changedPainting = originalPainting.ChangeLines(changingLine - 1);
            Painting overlappingPainting = this.generateOverlappingByChanged.Contains(originalPainting) ?
                changedPainting.ToOverlapping() : originalPainting.ToOverlapping();

            return new(originalPainting, overlappingPainting, changedPainting);
        }
    }
}